cssclasses:
- cornell-note
tags:
- italian
- design-patternsTODO PRENDERE INFO EXTRA DA
All page numbers (i.e. pag.) are from GoF's.
La dependency injection e' basata sull'Abstract Factory.
ESPANDERE con abstract factory
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Adatto una singola classe, staticamente, quindi proprio "quella classe".
"Un unico oggetto che puo' essere usato contemporaneamente con le due interfacce diverse (vecchia e nuova)".
Utile se devo adattare solo 1 metodo su 10: gli altri 9 mi vengono gratis, devo fare fatica solo su 1.
Adatto un oggetto compatibile con una interfaccia, una gerarchia; perche' posso settare diversi adapter, in base all'esigenza.
Due oggetti diversi.
Il nuovo non puo' piu' essere usato con interfaccia vecchia.
Si differenzia perche' sfrutta la composizione.
Posso aggregare piu' classi diversi, quindi c'e' maggiore flessibilita'.
Si hanno multipli costruttori in overloading.
Di difficile manutenibilita' nel momento in cui voglio diverse possibilita', perche' aumentano esponenzialmente i costruttori da dichiarare.
Problemi di concorrenza e sono obbligato ad accettare la mutabilita' dell'oggetto, perche' ho dei setter, quindi espongo lo stato.
Ho un costruttore privato con tutti i parametri, e una serie di metodi statici pubblici che al loro interno costruiscono l'oggetto in base a cio' che mi serve.
Posso dare nomi significativi a ogni metodo, per spiegare come crea l'oggetto, cosa che con il costruttore chiaramente non posso fare.
Classe Builder interna, con costruttore con soli parametri obbligatori. Si hanno poi variabili d'istanza con valori di default, sovrascrivibili tramite metodo, usando fluent interface.
public class Xxx {
private final TO optionalField1;
private final T1 mandatoryField;
private final T2 optionalField2;
private Xxx(Builder builder) {
mandatoryField = builder.mandatoryField;
optionalField1 = builder.optionalField1;
optionalField2 = builder.optionalField2;
}
public static class Builder {
private TO optionalField1 = defaultValue1;
private T1 mandatoryField;
private T2 optionalField2 = defaultValue2;
public Builder(T1 mf) {
mandatoryField = mf;
}
public Builder withOptionalField1(TO of) {
optionalFieldl = of;
return this;
}
// etc...
Usage
Xxx b = Xxx.Builder(mandatoryField)
.withOptionalField1(optionalField)
.build();
Disaccoppia chi genera la richiesta da chi puo' soddisfarla.
"Permette di definire una catena di potenziali gestori di una richiesta, non sappiamo a priori chi sara' in grado di gestirla effettivamente"
Evita l'antipattern dello switch case o degli if else
Ogni valutatore guarda se sa rispondere alla domanda, altrimenti manda avanti la domanda al prossimo: c'e' uno specifico ordine in cui le risposte sono ordinate.
Rimuovo la responsabilita' dalla classe client dell'identificazione di una certa situazione, la responsabilita' non e' sua. Il client e' chiuso rispetto alle modifiche ma aperto alle estensioni, perche' se devo cambiare la logica di gestione delle responsabilita' lui non viene toccato.
ESPANDERE
Analogia con il dynamic binding e il suo albero di navigazione per cercare l'implementazione del metodo.
Ottimo anche quando sviluppato in TDD, in quanto ogni nodo puo' essere sviluppato in isolamento.
Gestire uniformemente foglie e sotto-alberi.
"Non mi devo accorgere se sto interagendo con una foglia o un sotto-albero".
Aggiungere nuove funzionalita' o caratteristiche dinamicamente.
Distribuisce le responsabilita' lasciando ogni decorazione molto semplice.
Simile come struttura al Decorator, il composite mette assieme tante "cose", il Decorator decora una "cosa".
Notare l'1 nello schema, vicino a Component.
Possono esserci piu' ConcreteDecorator.
L'elemento finale di un Decorator pattern e' cio' che sto decorando, che puo' arrivare anche dopo una sequenza di decorazioni.
I vari oggetti decorati sono estranei gli uni agli altri.
Un esempio in Java sono gli InputStream.
Esempio di Decorator dal prof
Fornisce un modo di accedere agli elementi di un oggetto aggregatore in maniera sequenziale senza esporre la rappresentazione interna
Notare come condimentCost non dipende da chi sto decorando, dipende da ConcreteCondiment.
Fornisce una interfaccia unificata e semplificata a un insieme di interfacce separate.
Definisce un'interfaccia per creare un oggetto ma lascia alle sottoclassi la scelta su cosa creare.
L'MVC e' un set di pattern che collaborano assieme nello stesso design: Composite (view), Strategy (quale Controller iniettare, il Controller e' lo Strategy per la View), Observer (il Model e' observable e View e Controller sono observer).
The best way to think of MVC is as set of principles including the separation of presentation from domain logic and synchronizing presentation state through events (the observer pattern)
-- Martin Fowler
About "handling the triggering of synchronization between screen state and session state", MVC does it by making updates on the model and then relying of the observer relationship to update the views that are observing that model.
Il problema e' che c'e' una circolarita' di comunicazione, quindi si potrebbe preferire per questo l'MVP.
Un unico modello che rappresenta lo stato dell'applicazione
La View si aggiorna ogni volta che il Model cambia stato.
Tra Model e View c'e' una relazione tipo Observer.
Ascolta gli eventi dell'interfaccia, a fronte del quale eventualmente puo' richiedere una modifica dello stato.
Implementa quindi la Strategy di gestione di un evento. A seconda del Controller che associo ho un comportamento diverso.
Viene rimossa la dipendenza circolare.
Molto piu' facile da testare.
Il Presenter ha un riferimento a View e Model, agendo da middle man.
Per ogni View c'e' un Presenter.
Si puo' intendere quindi 1 classe View e 1 istanza Presenter, 1 classe View e 1 classe Presenter, tenendo presente i principi di buona scrittura del software.
Vedi Modalita' - pag 298 di Observer.
Il Presenter viene aggiunto come ascoltatore di cambi di stato sul Model.
model.addObserver(presenter)
Il presente viene aggiunto come gestore degli eventi sulle View
view.addHandler(presenter)
Puo' essere una buona idea operare una divisione di questo tipo:
State: rappresenta lo stato all'interno del pattern.
Model: e' Class Adapter dello State aggiungendo capacita' di Observer - pag 293.
Non sempre voglio che tutti i Presenter implementino lo stesso Presenter, ricordarsi di specializzare sfruttando l'eredita' multipla sulle interfacce.
Si puo' approcciare da diversi lati, ad esempio con diversi sotto-team che si avvicinano assieme al risultato.
O inizio dal Presenter (top down). Piuttosto che dal Model (bottom up).
In questo senso una buona strategia puo' essere partire dal Model delineando in una interfaccia quali sono i metodi che verranno implementati.
TODO RIMANDO A IMMUTABILITA'
Favorire un design con immutabilita' nel Model, per evitare deep copies.
Nel Presenter (ma...).
Importante che una volta validato il dato questa validazione non sia fatta, se si usa il contract based allora si puo' costruire un tipo che assume per costruzione la validazione del dato, es:
public record TimeOfRun(@NotNull String name, @NotNull Double time) {
public TimeOfRun {
if (name.isBlank) throw
if (time < 0) throw
}
}
E' il Presenter che istruisce la vista sul suo stato di errore.
Ma anche nel Model (...!).
Dipende da dove ha senso, ad esempio se ho un Model che gestisce due manche di sciatori, e mi arriva nel Model un nome di uno sciatore non presente nella prima manche allora questo lo posso gestire solo nel Model.
Puo' avere senso ad un certo stato di avanzamento nell'applicazione per qualcuno di deregistrarsi.
Vogliamo creare un oggetto che corrisponda al concetto "nessun valore" o "valore neutro"
public interface CardSource {
Card draw();
boolean isEmpty();
enum NULL implements CardSource {
INSTANCE;
public boolean isEmpty() {
return true;
}
public Card draw() {
assert !isEmpty();
return null;
}
}
}
Diversi modi di presentare una informazione, esempio
...il problema e' che ogni vista e' accoppiata alle altre perche' devono reagire al cambiamento
Per evitare questo estraiamo la parte comune: lo stato, e lo mettiamo in un oggetto a parte (Subject), osservato dagli altri (Observer)
Vogliamo rendere osservabile questo stato
public class State {
private double temp;
public State(double temp) {
this.temp = temp;
}
}
E' possibile avere due modalita' per avvertire gli observer di un cambiamento: pull e push
@Override
public void setTemp(double temp) {
if (this.temp != temp) {
this.temp = temp;
notifyObservers();
}
}
@Override
public void notifyObservers() {
for (Observer<Double> obs : observers) {
obs.update(this, temp); // qui si rendono disponibili entrambe le modalita'
}
}
Stato semplice
Stato complesso o parzialmente rilevante
Il subject fornisce metodi (getter) per accedere al proprio stato, lasciando agli observer il compito di scegliere cosa consultare.
Vantaggi: Più flessibile, perché ogni observer può decidere cosa gli serve. Ideale per stati complessi.
Il subject invia lo stato agli observer.
Vantaggi: semplice da implementare se lo stato è compatto e se gli observer richiedono tutte le informazioni.
Svantaggi: inefficiente se lo stato è complesso e gli observer sono interessati solo a una parte, o se vogliono solo essere notificati del cambiamento senza conoscerne i dettagli.
@Override
public void update(@Nullable Observable<Double> subject, @NotNull Double state) {
view.setValue(String.format("%.2f", strategy.convertFromCelsius(state)));
}
Sfrutta il fatto che in Java i campi degli
enumsono realizzati tramite degli oggetti costanti creati al momento del loro primo uso
public enum Singleton {
INSTANCE;
public void op() { ... }
}
Singleton.INSTANCE.op();
Nei diversi momenti ci sono diversi stati da poter utilizzare (state machine).
Meta stato (nell'esempio State)
Stato astratto ConcreteState)
Stato concreto
Gli stati non devono conoscersi a vicenda, e' il Context che puo' conoscerli.
Definisce una famiglia di algoritmi, e li rende (tramite encapsulation) tra loro intercambiabili.
Ad esempio usato nel sorting.
TODO ESPANDERE
cssclasses:
- cornell-note
tags:
- java
- static-binding
- dynamic-bindingprivate static final Comparator<TimeOfRun> COMPARATOR = Comparator
.comparingDouble(TimeOfRun::time)
.reversed() // refers to evertything until this point
.thenComparing(tr -> tr.name)
.reversed() // refers to evertything until this point
Bene disambiguare su tutti i campi. Perche' alcune classe che assumono l'implementazione di Comparable<T> potrebbero usare compareTo per vedere ad esempio se la chiave e' gia' esistente in una struttura dati.
TODO
Difference between checked and unchecked
Unchecked: se avvengono il programma non e' recoverable, quindi non ha senso fare try catch. Per lo piu' sono errori del programmatore.
// ci assicura che lo scanner venga chiuso
try (Scanner sc = new Scanner(input)) {
// code
}
Un ottimo modo per evitare duplicazione all'interno dei test. Anche i test sono codice.
Si tratta del modo con cui viene selezionato il metodo a partire da un tipo:
cssclasses: []
tags:
- software-engineering
- sweng
- nullability
- italianNullability e' la possibilità di assegnare un valore speciale null ad una variabile che indica un riferimento ad un oggetto.
Il tentativo di de-referenziare un null porta una NullPointerException.
null e' assegnabile a qualsiasi variabile di tipo riferimento, ne e' sottotipo. Quindi questo vuole dire che se non si interviene in qualche modo il compilatore non reagirà in alcun modo, e a runtime ci sarà una eccezione non gestita, con il conseguente halt dell'applicazione.
null per se non e' il problema, il problema e' il fatto che e' assegnabile a qualsiasi tipo riferimento.
Per mitigare questa situazione ci sono diverse soluzioni:
@NotNull e @Nullable - per esprimere il design by contract spiegato da Meyer, a runtime non ci sono più, inoltre sono gestibili da tool di analisi statica che possono usarle per fornire indicazioni utili durante lo sviluppo "[...] But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement."
-- Sir Tony Hoare
Si può gestire in due modi
Un codice non dovrebbe far uso di
nullnelle parti visibili, di interfaccia
Fail fast: Objects.requireNonNull esplicita che non accetto qualcosa come null, gestendolo sollevando una NullPointerException, pero' viene eseguito sempre, anche in produzione, può avere senso ai bordi di un contratto
public Card(Rank rank, Suit suit) {
this.rank = Object.requireNonNull(rank);
this.suit = Object.requireNonNull(suit);
}
}
Difensiva: gestione esplicita dei null, "sono capace di gestire null", programmazione difensiva, pero' viene eseguito sempre, anche in produzione, può avere senso ai bordi di un contratto
public Card(Rank rank, Suit suit) {
if (rank == null || suit == null) {
throw new IllegalArgumentException();
}
this.rank = rank;
this.suit = suit;
}
Mi assicuro che in sviluppo non mi arrivi un null, ma non e' compito mio:
public Card(Rank rank, Suit suit) {
assert rank != null && suit != null;
this.rank = rank;
this.suit = suit;
}
Intellij può generare if in questo caso, inoltre lascia aperta la porta a tool che potrebbero fare analisi statica grazie alla mia dichiarazione di intento:
private final @NotNull Rank rank;
private final @NotNull Suit suit;
public Card(@NotNull Rank rank, @NotNull Suit suit) {
this.rank = rank;
this.suit = suit;
}
Optional e' pensato per essere utilizzato nei tipi di ritorno, non troppo diverso da avere la necessita' di testare per null, sebbene sia espressa dal tipo la necessita' di gestire la situazione
Il seguente codice invece e' chiaramente completamente sbagliato, perché viene comunque creato un oggetto con le variabili d'istanza a null
public Card(Rank rank, Suit suit) {
if (rank != null && suit != null) {
this.rank = rank;
this.suit = suit;
}
}
cssclasses: []
tags:
- sweng
- reference-escaping
- encapsulation
- information-hiding
- italianThe point of encapsulation isn't really about hiding the data, but in hiding design decisions, particularly in areas where those decisions may have to change
Reference escaping - "pensavo di essere protetto dall'encapsulation, ma invece e' peggio perche' e' difficile da trovare"
Non sempre c'e' reference escaping, dipende se cio' che sto passando e' o meno un segreto, se non e' un segreto o se e' immutabile allora non c'e'.
Tipologie di relazioni:
Lo stato del Model, in Model View Presenter e' un segreto da proteggere, perche' se qualcuno lo modifica dall'esterno non posso notificare gli osservatori.
Normalmente le variabili di istanza devono essere private, chiaramente non vuol dire mettere getter e setter. Vedere lo stato puo' essere corretto, cio' che non e' corretto e' consentire la modifica.
Si prende un riferimento esterno senza farsene una copia. Chiaramente puo' succedere nei punti di contatto con l'esterno come setter o costruttori.
Oggetti immutabili possono essere condivisi.
Cosa vuol dire essere immutabili?
Non vuol dire essere stateless, vuol dire che non c'e' modo di cambiare lo stato dell'oggetto dopo la sua inizializzazione. String e' immutabile ma una volta creata non la modifico piu'.
Dare un nome ai concetti: type abstraction.
Things that are tightly coupled should be in the same component. Thinking of tell-don't-ask is a way to help programmers to see how they can increase this co-location.
Qui ne parlano un po' più approfonditamente:
Adhering to this notion of “Tell, Don’t Ask” is easier if you mentally categorize each of your functions and methods as either a command or a query
"Non chiedere i dati, ma di cosa vuoi che faccia sui dati"; vuol dire ad esempio minimizzare i getter e creare una funzione che provveda al vero obiettivo per il quale abbiamo creato il getter.
[...] start by designing classes based on their responsibilities, you can then progress naturally to specifying commands that the class may execute, as opposed to queries that inform you as to the state of the object.
We expect that the methods of a class are not closed to changes in the member variables of that class. However we do expect that any other class, including subclasses are closed against changes to those variables. We have a name for this expectation, we call it: encapsulation
Identify points of predicted variation and create a stable interface around them.
cssclasses: []
tags:
- swengTo ask is a query, to tell is a command
This principle concerns how objects interact, specifically regarding state and behavior. It emphasizes telling an object what to do, rather than asking it for its state and then making decisions based on that state externally.
Rule: instead of querying an object's state and then performing actions based on that state in the calling code, you should delegate that responsibility to the object itself by providing methods that encapsulate the action and the decision-making logic.
Goal: to improve encapsulation and place behavior closer to the data it operates on. This makes objects more autonomous and responsible for their own state and the rules governing it.
Asking
if (account.getBalance() > withdrawalAmount) {
account.debit(withdrawalAmount);
}
Telling:
account.withdraw(withdrawalAmount);
The logic if (balance > amount) is inside the withdraw method of the Account class.
Following "Tell, Don't Ask" often helps you adhere to the "Principle of Least Knowledge." When you tell an object to perform an operation (account.withdraw(amount)), you don't need to ask for its internal details (account.getBalance()), which might involve navigating through other objects, potentially violating LoD.
LoD is primarily about the structure of collaborations (who talks to whom), while TDA is about the nature of the interaction (commands vs. queries+decisions).
LoD restricts the scope of interactions, while TDA guides the style of interaction towards commands rather than state queries. Both contribute to building more robust, maintainable, and less coupled object-oriented systems.
For me, tell-don't-ask is a stepping stone towards co-locating behavior and data, but I don't find it a point worth highlighting.
Follow the links in the article because there's more to be learned.
cssclasses:
- cornell-note
tags:
- sweng
- solid
- italianTODO!!!!!!!!!!!!!!!!
FINISH extracting
https://condor.depaul.edu/dmumaugh/OOT/Design-Principles/
https://ieeexplore.ieee.org/author/37291880700
https://en.wikipedia.org/wiki/Robert_C._Martin
https://web.archive.org/web/20150906155800/http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
https://www.youtube.com/results?search_query=barbara+liskov
https://www.youtube.com/watch?v=v-2yFMzxqwU
https://duckduckgo.com/?q=Barbara+Liskov%2C+%E2%80%9CData+Abstraction+and+Hierarchy%2C%E2%80%9D+SIGPLAN+Notices%2C+23%2C5+(May%2C+1988).&atb=v340-1&ia=web
https://se.inf.ethz.ch/~meyer/publications/old/dbc_chapter.pdf
https://en.wikipedia.org/wiki/SOLID#cite_note-11
https://vimeo.com/97514630
https://www.youtube.com/watch?v=TMuno5RZNeE
https://www.youtube.com/watch?v=BSaAMQVq01E
When considering whether a particular design is appropriate or not, one must not simply view the solution in isolation. One must view it in terms of the reasonable assumptions that will be made by the users of that design.
A class should have one, and only one, reason to change
Why was it important to separate these two responsibilities into separate classes? Because each responsibility is an axis of change.
When the requirements change, that change will be manifest through a change in responsibility amongst the classes. If a class assumes more than one responsibility, then there will be more than one reason for it to change. If a class has more then one responsibility, then the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class’ ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed.
"Each software module should have one and only one reason to change", but then "What defines a reason to change?".
"Incoraggia un'alta coesione interna (tutti i metodi hanno a che fare con lo stesso obiettivo) e un alto disaccoppiamento esterno, porta ad una buona modularizzazione"
"This principle is about people."
This is the reason we do not put SQL in JSPs. This is the reason we do not generate HTML in the modules that compute results. This is the reason that business rules should not know the database schema. This is the reason we separate concerns.
"Gather together the things that change for the same reasons. Separate those things that change for different reasons."

The foundation for building code that is maintainable and reusable
You should be able to extend a classes behavior, without modifying it
When the creation of a derived class causes us to make changes to the base class, it often implies that the design is fault, indeed it violates the Open-Closed principle
Permette di ottenere:
Dynamic binding e' un aspetto chiave di OOP, perche' permette di chiamare codice non ancora scritto: cioe' riconoscendo il tipo concreto dal tipo apparente solo a runtime, tengo aperta la possibilita' di future estensioni.
if you have a program that works correctly with a base class, then it should continue to work correctly if you replace the base class with any of its derived classes
S sottotipo di T:
S non devono essere piu' stringenti delle precondizioni dei metodi di TS non devono essere piu' larghe delle postcondizioni dei metodi di TVoglio evitare che chi cita una classe e vede una possibilita' (metodo), si ritrovi con una classe figlia che questa possibilita' non ce l'ha.
If a function violates LSP then that function uses a reference to a base class, but must know about all the derivatives of that base class.
In order for the LSP to hold, and with it the Open-Closed principle, all derivatives must conform to the behavior that clients expect of the base classes that they use.
Siamo interessati al comportamento dei moduli tra di loro, comportamento su cui gli utilizzatori dipendono.
In Design by Contract (Bertrand Meyer) methods declare preconditions and postconditions:
The rule for these conditions, for derivatives, is:
...when redefining a routine in a derivative, you may only replace its precondition by a weaker one, and its postcondition by a stronger one.
So:
Clients should not be forced to depend on methods they do not use
Make fine grained interfaces that are client specific
"Offer different views of the same type."
"Polimorfismo e' un modo per esprimere di fronte alla stessa interfaccia comportamenti diversi. Grazie al polimorfismo possiamo mostrare lo stesso cosa nascondendo diversi come."
-- Jessica Vecchia
Fa in modo che il client ottenga la dipendenza minima dalla classe che vuole utilizzare, vuole utilizzare solo certi aspetti che la classe implementa, non tutti.
Questo principio permette di raggiungere un grado basso di accoppiamento tra gli oggetti.
update() che e' l'unico interesse del SubjectDepend on abstractions, not on concretions
I moduli di alto livello non dovrebbero dipendere dai moduli di basso livello: entrambi dovrebbero dipendere da astrazioni.
Le astrazioni non dovrebbero dipendere dai dettagli. Programmare verso le interfacce.
"Depend on stuff more concrete than me"
This is pure gold: https://blog.cleancoder.com/uncle-bob/2016/01/04/ALittleArchitecture.html
Identificare gli aspetti della applicazione che cambiano e separarli da cio' che rimane fisso
In questo caso usiamo due volte lo Strategy.
Fin qua tutto ok.
Il problema nasce quando si aggiunge RubberDuck e successivamente si aggiunge un metodo fly() a Duck: RubberDuck si ritrova a poter volare!
Override non e' una soluzione perche' mi ritrovo a dover implementare uno "stub" dentro RubberDuck, sto violando Liskov substitution principle: ho una sottoclasse che non sa fare qualcosa che la superclasse sa fare.
Definire diverse interfacce come Quackable, Swimmable, ..., con metodi di default e' meglio ma devo far implementare a MallardDuck e RedheadDuck le interfacce in base a cosa possono fare, ma mi porta a duplicazione.
Delego!
Duck contiene (aggrega) al suo interno come si comporta rispetto al fare "quack".
Quindi non devo avere piu' ragioni per cambiare: "cambia modo di fare quack o di volare?" se sono nella classe sono due motivi diversi per cambiare la classe.
tags:
- italian
- swengObiettivo e' ridurre l'accoppiamento tra gli oggetti e promuovere la modularità del codice.
E' anche chiamato principle of least knowledge, o in modo colloquiale riassunto con "gli amici dei miei amici non sono miei amici".
Anche citato in "tell don't ask".
Maggiore e' il numero di oggetti con cui parli, maggiore e' la probabilità di rompersi quando questi oggetti cambiano.
The fundamental goal [...] is to write shy code, code that doesn’t reveal too much of itself to anyone else and doesn’t talk to others any more than is necessary. Shy code keeps to itself, not like that gossipy neighbor who’s involved in everyone else’s comings and goings.
Limitare le interazioni tra gli oggetti ad un numero ristretto di amici stretti.
Meno conosco gli altri oggetti piu' e' facile che possano cambiare.
Il codice di un metodo dovrebbe accedere soloLimitazioni analoghe a quella che in programmazione funzionale e' una funzione pura a:
thisUn esempio di non applicazione potrebbe essere: "Faccio un get, manipolo cio' che ho ottenuto, e faccio un set."
Protected variation
Open close principle
Protection against change to the existing code and design at variation and evolution points
cssclasses:
- cornell-note
tags:
- inheritance Meccanismo che mi permette di stabilire un legame, sottotipo se rispettiamo Liskov, su questo posso costruire Polimorphism.
Privilegiare composizione sull'ereditarieta' ESPANDERE
In statically typed languages like C++, one of the key mechanisms that supports abstraction and polymorphism is inheritance. It is by using inheritance that we can create derived classes that conform to the abstract polymorphic interfaces defined by pure virtual functions in abstract base classes.
tags:
- sql
- postgres
- postgresql
- query
- databases
- italianStructured Query Language
Linguaggio dichiarativo.
DDL: comandi per creare strutture per l'ossatura della base di dati (CREATE TABLE, CREATE VIEW, ...)
DML: istruzioni (INSERT, DELETE, UPDATE, ...)
DQL: SELECT
DCL: comandi sul controllo
lower trasforma la stringa in lowercaselike ilike insensitive liketrim rimuove gli spazi prima e dopoltrim spazi a sinistrartrim spazi a destrabetween ad esempio ... vote BETWEEN 1 and 10;in ad esempio ... lower(genre) in ('drama', 'thriller', 'crime')distinct
SELECT DISTINCT movie FROM imdb.genre (elimina i duplicati, rendendo il risultato "come fosse in algebra relazionale")SELECT DISTINCT movie, genre FROM imdb.genre perché DISTINCT lavora sull'intera clausola SELECTorder by ordina ciò che restituiamo, e' l'ultima cosa che viene fatta; si può specificare DESC per più attributi in base alle necessita'ORDER BY 2 per riferirsi alla colonna 2extract permette di prendere parti di una data come ad esempio WHERE extract(YEAR FROM birth_date) = '1971'as e' l'equivalente di :: e' l'operatore di cast SELECT extract(year) from birth_date)::char(4) from imdb.personis null unico modo per confrontare con NULL ALL da true solo se il predicato e' verificato per tutti i valori restituiti dalla sotto query ad esempio nella seguente ammettendo che la sub-query ritornasse più valori, allora un record per poter essere restituito dovrebbe essere maggiore di tutti questi valoriSELECT id, official_title, length
FROM imdb.movie
WHERE length > ALL (
SELECT length
FROM imdb.movie
WHERE official_title = 'Inception'
);
ANY da true se il predicato e' verificato per almeno un valore restituito dalla sotto querymin/max (agg)avg (agg)sum (agg)count (agg) cardinalità di una relazione
COUNT(*) conta tutte le righeCOUNT(attributo) conta le occorrenze in cui l'attributo non e' NULL, sempre bene contare su chiave esterna per essere sicuro che sia NOT NULLgroup by (agg) partizionare i record in base ad un criteriohaving filtra sul risultato del gruppo, quindi su operatori aggregati o sugli attributi del gruppo; come ottimizzazione e' meglio usare una WHERE prima di arrivare alla GROUP BY in modo da raggruppare su meno record, quindi e' sconsigliato usare la HAVING al suo posto(agg) sono operatori di aggregamento, ignorano il NULL; vengono eseguiti alla fine; posso proiettare solo attributi aggregati o prodotti da un operatore di aggregamento.
Non si possono fare aggregati di aggregati.
La precedenza degli operatori logici viene valutata da sinistra verso destra, quindi
SELECT *
FROM imdb.movie
WHERE year '2010' OR length >= 60 AND length <= 120;
Prima valuta year '2010' OR length >= 60 e poi il risultato di questa in AND.
% segnaposto per una stringa di qualsiasi lunghezza
_ segnaposto per una stringa di lunghezza 1
SELECT *
FROM imdb.movie, imdb.produced
Cardinalità totale e' uguale a cardinalità movie (1033) per cardinalità produced (1332).
Sono una selezione sul prodotto cartesiano.
SELECT *
FROM imdb.movie m, imdb.produced p
WHERE m.id = p.movie
movie.id chiave primaria, produced.movie chiave esterna.
Cardinalità e' uguale a cardinalità di produced.
Sintassi alternativa
SELECT m.id, m.official_title
FROM imdb.movie m
INNER join imdb.produced p ON m.id = p.movie
WHERE country = 'USA'
a JOIN b ON c restituisce i record di a e b nel prodotto cartesiano che soddisfano c.
Eventuali WHERE vengono applicate dopo la JOIN!
Aggiunge al join eventuali record che non hanno alcuna corrispondenza nella tabella di destra (nel caso di LEFT JOIN).
Prima fa un INNER JOIN applicando le clausole ON, poi aggiunge i record spuri (che sono quelli della tabella "dall'altro lato" per cui non e' stato trovato un corrispondente).
Ad esempio per la richiesta "selezionare paesi nei quali non sono prodotti film":
SELECT c.iso3
FROM country c
LEFT JOIN produced p ON c.iso3 = p.country
WHERE p.country IS null
La cardinalità quindi include anche i record senza corrispondenza.
FULL JOIN combina LEFT e RIGHT.
Oggetti derivati formati a partire dai dati nelle tabelle, per offrire delle proiezioni dei dati tipicamente appartenenti a tabelle diverse.
CREATE VIEW movie_person as (
SELECT *
FROM movie
INNER JOIN crew on movie.id crew.movie
INNER JOIN person on person.id = crew.person
)
A questo punto trovare le persone di inception diventa quanto segue.
Prima viene eseguita movie_person e poi viene eseguita la seconda query (nessuna duplicazione dei dati).
SELECT *
FROM movie_person
WHERE official_title ilike 'inception' AND p_role 'actor'
Controllare i costi di una query, mostra l'albero di esecuzione della query.
Si parte a leggere dalle foglie.
EXPLAIN ANALYZE ...
Molto inefficiente
TODO espandere
La tabella sim descrive similarità tra pellicole, la colonna cause spiega il motivo di questa similarità.
Sono interessato alla pellicola 0013444, le sono simili quelle che rispondono a
SELECT movie2 FROM sim WHERE movie1 = '0013444'
Ma sono simili a 0013444 anche le pellicole simili a quelle restituite dalla query precedente, cioè se 0018756 e' restituito dalla query precedente, anche le query simili a 0018756 sono indirettamente simili a 0013444.
Magari vorrei fermarmi ad una "distanza 3". Posso interpretare le pellicole come nodi, un arco e' presente se c'e' una relazione di somiglianza tra le due pellicole.
WITH RECURSIVE t(n) AS (
SELECT 1
UNION all
SELECT n+1 FROM t)
SELECT n FROM t
t(n) specifica che n e' il nome di cosa la query restituisce.
SELECT 1 e' il passo base, SELECT n+1 FROM t e' il passo ricorsivo.
Tutte le query ricorsive quindi seguono questo schema, con la UNION tra i due passi.
UNION ALL rimuove duplicati, UNION li tiene.
Il problema di questa query e' che non si ferma mai, quindi ne limitiamo l'esecuzione con la WHERE.
WITH RECURSIVE t(n) AS (
SELECT 1
UNION all
SELECT n+1 FROM t WHERE n < 10)
SELECT n FROM t
La differenza tra UNION e UNION ALL e' nella gestione dei duplicati, la seconda gestisce anche i duplicati.
Supponiamo di essere interessati ad una organizzazione dei generi che sia gerarchica.
CREATE TABLE genre_taxonomy {
genre_name VARCHAR PRIMARY KEY,
genre_parent VARCHAR
}
ALTER TABLE genre_taxonomy
ADD CONSTRAINT parent_fk FOREIGN KEY (genre_parent)
REFERENCES genre_taxonomy(genre_name)
Serve ALTER TABLE perché e' un riferimento alla tabella stessa, quindi va prima creata la tabella, e poi si può introdurre la foreign key.
Relazione 1 a N:
- thriller
- noir
- poliziesco
- spionaggio
- cronaca nera
- splatter
Restituire i generi di 'poliziesco'
WITH RECURSIVE search_parent(the_genre, parent_genre) AS (
SELECT genre_name, genre_parent
FROM genre_taxonomy
WHERE genre_name = 'poliziesco'
union
SELECT sp.the_genre, gt.genre_parent -- nota gli stessi nomi dei parametri
FROM search_parent sp
JOIN genre_taxonomy gt ON sp.genre_parent = gt.genre_name
)
SELECT * FROM search_parent
Si ferma grazie al JOIN che non produce una riga quando trova un NULL nella sua condizione di ON.
Restituire i primi due generi di 'poliziesco', quindi mi fermo "prima di arrivare in cima"
Aggiungo un parametro distance
WITH RECURSIVE search_parent(the_genre, parent_genre, distance) AS (
SELECT genre_name, genre_parent, 1
FROM genre_taxonomy
WHERE genre_name = 'poliziesco'
union
SELECT sp.the_genre, gt.genre_parent, sp.distance + 1
FROM search_parent sp
JOIN genre_taxonomy gt ON sp.genre_parent = gt.genre_name
WHERE distance < 1
)
SELECT * FROM search_parent
WITH RECURSIVE search_sim(movie, t_movie, distance) AS(
SELECT movie1, movie2, 1
FROM sim
WHERE movie1 = '0013444'
UNION
SELECT ss.movie, si.movie2, distance + 1
FROM search_sim ss
JOIN sim si ON ss.s_movie = si.movie1
WHERE distance < 3
)
SELECT official_title
FROM imdb.movie
WHERE year = '2010';
SELECT *
FROM imdb.movie
WHERE official_title LIKE '%murder%'
SELECT *
FROM imdb.produced p1, imdg.produced p2
WHERE p1.movie = p2.movie AND p1.country <> p2.country
Pero' in questo caso ottengo duplicati, quindi dovrei rimuoverli, posso risolvere usando il <
SELECT p1.movie, p1.country as country1, p2.country as country2
FROM imdb.produced p1, imdg.produced p2
WHERE p1.movie = p2.movie AND p1.country < p2.country
SELECT *
FROM location l1, location l2
WHERE l1.person = l2.person AND
l1.d_role <> l2.d_role AND l1.country <> l2.country
Ma se la lascio cosi ha duplicati, per rimuoverli uso
SELECT *
FROM location l1, location l2
WHERE l1.person = l2.person AND
l1.d_role = 'B' AND l2.d_role = 'D' AND
l1.country <> l2.country
SELECT movie.id
FROM movie
EXCEPT
SELECT DISTINCT material.movie
FROM material
La seguente e' sbagliata! Scorre la tabella produced cercando un record che contemporaneamente abbia country 'ITA' e 'USA'
SELECT *
FROM produced
WHERE country = 'ITA' AND country = 'USA'
Viene semplicissimo con l'intersezione occhio a non usare * perché in un caso filtriamo country con ITA e nell'altro con USA quindi saranno sempre diversi, quindi mai intersecabili
SELECT movie FROM produced WHERE country = 'ITA'
INTERSECT
SELECT movie FROM produced WHERE country = 'USA'
SELECT id, official_title
FROM produced JOIN movie ON movie = id
WHERE country = 'ITA'
INTERSECT
SELECT id, official_title
FROM produced JOIN movie ON movie = id
WHERE country = 'USA'
Metto anche id nella SELECT per avere una intersezione solo quando anche l'id (che e' chiave) e' uguale, altrimenti potrei avere duplicati.
Posso farlo anche con una CTE .
Common Table Expressions
WITH mp AS (
SELECT * FROM produced INNER JOIN movie ON movie = id
)
SELECT id, official_title FROM mp WHERE country = 'ITA'
INTERSECT
SELECT id, official_title FROM mp WHERE country = 'USA'
Il beneficio e' creare un oggetto in memoria, temporaneo.
Utile quando ho bisogno di diversi componenti.
Oppure ancora
SELECT id, official_title FROM movie WHERE id IN (
SELECT movie FROM produced WHERE country = 'ITA'
INTERSECT
SELECT movie FROM produced WHERE country = 'USA'
)
Oppure ancora con self join (che e' anche un razzo)
SELECT usa.movie
FROM produced usa
JOIN produced ita ON usa.movie = ita.movie
JOIN movie ON usa.movie = id
WHERE usa.country = 'USA' AND ita.country = 'ITA'
La seguente non va bene senza ANY perché potrei avere più risultati dalla inner query
SELECT id, official_title, length
FROM imdb.movie
WHERE length > ANY (
SELECT LENGTH
FROM imdb.movie
WHERE official_title = 'Inception'
)
O anche
SELECT DISTINCT m1.id, m1.official_title
FROM movie m1 JOIN movie m2
ON m1.length > m2.length
WHERE m2.official_title = 'Inception'
TODO e' possibile usare SELF JOIN per emulare il comportamento della ALL?
SELECT p.movie
FROM produced p
WHERE p.country = 'ITA' AND movie NOT IN (
SELECT movie
FROM produced
WHERE country <> 'ITA'
)
Oppure con la sottrazione
SELECT movie FROM produced WHERE country = 'ITA'
EXCEPT
SELECT movie FROM produced WHERE country <> 'ITA'
Oppure con join esterno
WITH itamovies AS (
SELECT p.movie
FROM produced p
WHERE p.country = 'ITA'
),
nonitamovies AS (
SELECT movie
FROM produced
WHERE country <> 'ITA'
)
SELECT *
FROM itamovies LEFT JOIN nonitamovies ON itamovies.movie = nonitamovies.movie
WHERE nonitamovies.movie IS NULL
Oppure senza WITH
SELECT DISTINCT itamovies.*
FROM produced itamovies
LEFT JOIN produced nonitamovies ON itamovies.movie = nonitamovies.movie AND nonitamovies.country <> 'ITA'
WHERE itamovies.country = 'ITA' AND nonitamovies.movie IS NULL
La seguente non troverebbe i film che non hanno genere
SELECT id, official_title, genre
FROM movie
JOIN genre ON movie.id = genre.movie
quindi
SELECT id, official_title, genre
FROM movie
LEFT JOIN genre ON movie.id = genre.movie
SELECT id, given_name, country
FROM person
LEFT JOIN location ON person.id = location.person
WHERE d_role = 'D'
La WHERE viene valutata sull'effetto del JOIN, quindi lo annulla, perché per i record spuri d_role sarebbe NULL, quindi la query non e' corretta perché non aggiunge i NULL.
Possibile soluzione in cui filtriamo prima
WITH deaths AS (
SELECT *
FROM location l
WHERE d_role = 'D'
)
SELECT id, given_name, country
FROM person
LEFT JOIN deaths ON person.id = deaths.person
Oppure imponiamo il filtro nel momento del join
SELECT id, given_name, country
FROM person
LEFT JOIN location ON person.id = location.person AND d_role = 'D'
"Da tutte le possibili combinazioni, togliamo le pellicole che hanno un genere in comune"
SELECT DISTINCT g1.movie, g2.movie
FROM genre g1 JOIN genre g2 ON g1.movie > g2.movie
except
SELECT DISTINCT g1.movie, g2.movie
FROM genre g1 JOIN genre g2 ON g1.movie > g2.movie
WHERE g1.genre = g2.genre
oppure (notare l'uso di c nella Query correlata)
WITH couples as (
SELECT DISTINCT g1.movie AS movie1, g2.movie AS movie2
FROM genre g1 JOIN genre g2 ON g1.movie > g2.movie
)
SELECT *
FROM couples c
WHERE NOT EXISTS (
SELECT * ;; irrilevante cosa abbiamo qui
FROM genre g1, genre g2
WHERE g1.movie = c.movie1 AND g1.movie = c.movie2 AND g1.genre = g2.genre
)
Tabelle interessate: produced e released
Un movie m viene inserito nel risultato se non esiste un record della tabella produced p per il quale esiste un record di release relativo allo stesso movie e paese di p
SELECT id, official_title
FROM movie m
WHERE NOT EXISTS (
SELECT *
FROM produced p
WHERE p.movie = m.id AND EXISTS (
SELECT *
FROM released r
WHERE r.movie = m.id AND p.country = r.country
)
)
Utile quando devo contemporaneamente far valere più vincoli. Con gli operatori insiemistici non me la cavo perché devo controllare le condizioni su ogni singolo record. TODO ESPANDERE Con esempio e spiegazione
Oppure
SELECT id, official_title
FROM movie m
WHERE NOT EXISTS (
SELECT *
FROM produced p JOIN released r ON p.movie = r.movie AND
p.country = r.country
WHERE p.movie = m.id
)
SELECT max(length)
FROM movie
La seguente va in errore perché nell'aggregare perdono il riferimento alla tupla che contiene quel valore, inoltre potrei avere più di un record con quel valore, quindi devo aggregare tutto ciò che voglio selezionare
SELECT id, official_title, max(length) FROM movie
Un modo per farlo e'
SELECT * FROM movie WHERE length = (
SELECT MAX(length) as durata_massima FROM movie
)
Oppure anche
WITH mmax AS (
SELECT MAX(length) as durata_massima FROM movie
)
SELECT m.*
FROM movie JOIN mmax ON m.length = mmax.durata_massima
Non serve specificare WHERE year IS NOT NULL (vedi descrizione COUNT)
SELECT count(year) FROM movie
Invece cosi serve
SELECT COUNT(*) FROM MOVIE WHERE year IS NOT NULL
SELECT COUNT(DISTINCT official_title) FROM movie
SELECT sum(length) / count(length)
FROM movie
WHERE year = '2010'
Conteggia in base ad ogni gruppo trovato dalla GROUP BY
SELECT year, COUNT(*)
FROM movie
GROUP BY year
SELECT COUNT(*)
FROM crew
GROUP BY movie, c.p_role
La seguente pero' non tiene conto delle pellicole senza valutazioni, perché dentro rating ho solo pellicole già valutate
SELECT movie, COUNT(*)
FROM rating
GROUP BY movie
La seguente produce il risultato atteso ma e' convoluta
SELECT movie, COUNT(*)
FROM ratingGROUP BY movie
UNION
(
SELECT id, 0
FROM movie
EXCEPT
SELECT movie, 0
FROM rating
)
Nella seguente occhio a non usare COUNT(*) perché altrimenti conterebbe le righe, invece usando COUNT(r.movie) conteggio quando la riga non e' spuria.
SELECT movie, COUNT(r.movie)
FROM movie m LEFT JOIN rating r ON m.id = r.movie
GROUP BY id
SELECT movie, MAX(score / scale)
FROM movie m LEFT JOIN rating r on movie.id = rating.movie
GROUP BY id
ORDER BY 2 DESC
MAX(COUNT(*)) non si può fare.
La seguente e' sbagliata, perché potrebbero esserci più record con il valore massimo.
DISTINCT MOVIE per rimuovere attori con più di un ruolo nello stesso film.
SELECT person, COUNT(DISTINCT movie)
FROM crew
WHERE p_role = 'actor'
GROUP BY person
ORDER BY 2 DESC
LIMIT 1
Soluzione
WITH recitazioni AS (
SELECT person, COUNT(DISTINCT movie) AS n_partecipazioni
FROM crew
WHERE p_role = 'actor'
GROUP BY person
)
SELECT id, given_name, n_partecipazioni
FROM person p JOIN recitazioni r ON p.id = r.person
WHERE n_partecipazioni = (
SELECT MAX(n_partecipazioni) AS max_partecipazioni
FROM recitazioni)
Soluzione alternativa
SELECT person, COUNT(DISTINCT movie) AS n_partecipazioni
FROM crew
WHERE p_role = 'actor'
GROUP BY person
HAVING COUNT(DISTINCT movie) >= ALL (
SELECT COUNT(DISTINCT movie)
FROM crew
WHERE p_role = 'actor'
GROUP BY person
)
WITH movie_cast AS (
SELECT movie, COUNT(DISTINCT person) AS n_person
FROM crew
WHERE p_role in ('actor', 'director')
GROUP BY movie
),
avg_genre AS (
SELECT genre, AVG(n_person) as avg_cast
FROM movie m LEFT
LEFT JOIN genre g ON m.id = g.movie
LEFT JOIN movie_cast mc ON m.id = mc.movie
GROUP BY genre
)
SELECT m.id, official_title, g.genre, n_person
FROM movie m
LEFT JOIN genre g ON m.id = g.movie
LEFT JOIN movie_cast mc ON m.id = mc.movie
WHERE mc.n_person > (
SELECT avg_cast
FROM avg_genre
WHERE g.genre = avg_genre.genre
)
Tabelle utili
In algebra e'
SELECT id, given_name
FROM person p
WHERE NOT EXISTS (
SELECT *
FROM genre g
WHERE g.genre = 'Crime' AND NOT EXISTS (
SELECT *
FROM crew c
WHERE p_role = 'actor' AND p.id = c.person AND g.movie = crew.movie
)
)
Questa prima query e' in grado di dire quali ruoli e la persona ha svolto e in che film...
SELECT DISTINCT c1.person, c1.p_role, c2.p_role, c1.movie
FROM crew c1
JOIN crew c2 ON c1.person = c2.person
WHERE c1.p_role <> c2.p_role AND c1.movie = c2.movie
ORDER BY c1.person
...mentre questa query e' in grado di dire che la data person ha svolto più ruoli all'interno del film, ma non sa dire quali per via dell'aggregazione che fa perdere informazione; per contro questa query e' più performante della precedente
SELECT person, movie
FROM crew
GROUP BY movie, person
HAVING COUNT(DISTINCT p_role) > 1;
ANY e' opzionale nel caso di un solo record ritornato
WITH cast_crew_num AS (
SELECT movie, COUNT(*)
FROM crew
GROUP BY movie
),
mean AS (
SELECT AVG(count) AS m
FROM cast_crew_num
)
SELECT *, (SELECT m FROM mean)
FROM cast_crew_num
WHERE count > (SELECT m FROM mean)
cssclasses:
- cornell-note
tags:
- databases
- italianSfide nella gestione dei dati:
Tutti questi vengono gestiti dai DBMS.
Vincoli - leggi che voglio applicare per mantenere l'integrità dei dati
In realtà
La tabella
La struttura delle tabelle deve cambiare solo quando cambiano i requisiti.
Rigidita' dei database.
Se c'e' un rating allora vuol dire che c'e' uno e un solo film, per via del fatto che c'e'
DDL - Data Definition Language - lavora sullo schema
DML - Data Manipulation Language - lavora sui dati
Indipendenza - astrazione di come i dati vengono memorizzati su disco, che ci consente di disinteressarci di come sono fisicamente distribuiti
DQL - Data Query Language - ogni utente ha una vista dei dati che e' di interesse per quello specifico utente
Controllo della concorrenza - condivisione magari sugli stessi dati o addirittura sullo stesso record (lock, transazioni)
DCL - Data Control Language - controllo degli accessi
Database sono i dati, DBMS e' il software.
DBMS - sistema software in grado di gestire collezioni di dati che siano grandi, condivise, persistenti assicurando la loro affidabilità e privatezza, deve essere efficiente ed efficace
Una base di dati (o database) e' una collezione di dati gestita da un DBMS
Definizione di tabella
Basato sul concetto di relazione matematica , intesa come sottoinsieme del prodotto cartesiano fra due o più insiemi di dati detti domini
Dove
Posso definire una relazione
che avrà
Posso scrivere
dove nella prima riga ho la relazione con i domini, nella seconda riga ho un esempio di tupla, dove
Insiemi degli schemi di tutte le relazioni che lo costituiscono, ogni relazione ha poi il suo schema:
Dove
Lo schema non cambia se cambiano i requisiti, l'istanza si e anche spesso
Istanza della base di dati e' l'insieme dei record nelle relazioni.
Estensionale ed intensionale
La parte estensionale di una tabella sono i suoi dati. La parte intensionale e' il suo schema.
Per ogni tabella esiste sempre un identificatore, al peggio e' composto da tutti i valori degli attributi che definiscono la tabella.
Grado di relazione: quanti attributi sono definiti sulla relazione
Cardinalità di una relazione: quante tuple ci sono nella relazione
Caratteristiche che i dati devono avere per poter essere ammessi.
Predicato il cui valore di verità e' verificato all'inserimento di un possibile record nella relazione.
Avere un tipo di valore DATE già fornisce vincoli su come deve presentarsi il dato.
NULL e NOT NULLConsentire l'inserimento di NULL e' il default.
Ad esempio
Potrebbe avere come vincoli di integrità
Valori che referenziano valori identificativi referenziati altrove.
Una qualsiasi combinazione di attributi che garantisce l'univocità dei valori nelle ennuple della relazione.
Data
La seguente relazione
avrebbe come superchiavi
Una chiave e' una superchiave minimale: non posso toglierle alcun attributo senza che sia una chiave.
Non posso togliere attributi alla chiave mantenendone la caratteristica di chiave, ad esempio a
Tutte le combinazioni che contengono
Vincolo di entity integrity; e' una chiave sulla quale non sono possibili valori NULL. Ogni relazione ha una e una sola chiave primaria.
Metto in relazione dati appartenenti ad entità diverse. Permette l'operazione di JOIN
Possibili problemi quando si fa un INSERT o UPDATE:
Quando si fa una DELETE su una tabella referenziata si può avere un problema solo se esistono record nella tabella referenziante; NO ACTION.
Politiche NO ACTION e CASCADE servono a mantenere l'integrità referenziale.
Un film viene proiettato in un cinema in un certo giorno e orario:
con:
FOREIGN KEY (c_name, c_city) REFERENCES cinema(name, city) ON UPDATE CASCADE ON DELETE NO ACTION; per evitare la chiave esterna composta, si può considerare l'uso di una chiave atomicaSe una tabella non e' referenziata da altre tabelle non serve un
Data
| id | firstname | lastname | father |
|---|---|---|---|
| 1 | Mario | Montanelli | [NULL] |
| 2 | Stefano | Montanelli | 1 |
FOREIGN KEY (father) REFERENCES person(id); inoltre la chiave esterna può essere NULL.
VECCHIO file
Mathematical relations between sets (domains) via a Cartesian product.
Data is organized in rows, all rows are different from each other (since they are the result of a Cartesian product).
Keep the schema as fixed as possible, so that applications using it do not need to change accordingly.
Given
A relation is a subset of that product such that
Absence of a value in the domain.
We can't use it to compare values, an attribute
Everything that does not falsify a predicate it's true.
Only something that verify a predicate it's true.
A predicate that for each instance could return true or false.
A dependency
"Which are the minimum attributes I need to determine that attribute?"
This allows to determine which attributes go in which tables.
Of course we could only recognize functional dependencies if we know the domain of the application, if we don't know anything it's very hard to do a good job in identifying them.
Key question to ask ourself to see if there's a functional dependency: "left side of this hypothetical dependency identifies precisely the right side?" if not then there isn't a functional dependency.
We want to avoid redundancy in the information.
A database is well formed if it has 1NF, 2NF, 3NF.
As a rule of thumb:
Only atomic attributes.
We don't have a relational database if we don't have this NF.
Depends on 1NF.
Every non-key attribute must depend on the primary key, as a whole, and not just on a subset of it.
Normalization: put the subset of the key and the attribute in a new table.
If the key is a single attribute then we always have 2NF.
Depends on 2NF.
Transitive dependency:
2NF and no non-prime attribute in
In other words: we don't want two non-key attributes on the left AND on the right of a functional dependency. They all have to be on the left side.
All non prime attributes must depend on a super-key.
The key in the decomposed relations must be a super-key in the original relation.
Goal is to start from a big universal relation and finish to a scheme with multiple relations, which respect the normal forms.
There is a procedure to follow, which has 3 rules:
The algorithm is:
Anomalies and redundancies arise when we have functional dependencies like
To avoid redundancies and anomalies, we have to put our relation in BCNF.
cssclasses:
tags:
- databases
- italianCome costruire un database, come progettarlo.
Entità sono rettangoli
Associazioni sono rombi, più importante il significato rispetto al suo nome, cioè quali entità associa
Per ogni relazione ci sono due cardinalità
Si modelli un diagramma ER che descrive una realtà ospedaliera in cui i medici curano i pazienti
id, ma avrò tessera_sanitariaresidenza: non voglio concatenare tutto in una stringa(x, y) dove x e' la cardinalità minima, y la cardinalità massima
N(0,N) e (1,N) sarebbe una cardinalità molti a molti)Seguono alcuni esempi con richieste differenti
| Esempio | Medico - Paziente | Paziente - Medico | Tipo |
|---|---|---|---|
| Un medico cura non più di 1000 pazienti, e un paziente non può avere più di 10 medici curanti | (0, 1000) | (1, 10) | N N |
| Un medico cura almeno 10 e non più di 1000 pazienti, e un paziente non può avere più di un medico curante | (10, 1000) | (1, 1) | 1 N |
In una N a N una associazione viene tradotta con una tabella con un identificativo per parte.
In una 1 a N la chiave esterna va nell'entità che ha 1 come cardinalità massima.
"Ogni medico ha un supervisore che e' un altro medico", produrra' due modi di leggere l'associazione:
Ed e' una 1 a N.
"I medici lavorano in reparti all'interno di ospedali, ogni reparto ha un primario scelto tra i medici che vi lavorano"
Mettere primario come attributo di Reparto e' un errore, perché lo identificherei con la sua matricola, e quindi sarebbe un Medico, quindi modello come una associazione.
Reparto da solo non e' in grado di descriversi univocamente.
La cardinalità minima e' sempre (1, 1).
TODO prendi da appunti di lezione gli altri schemi, ad esempio dove msotra attributi sulle associazioni
TODO mettere rimando al vincolo extra schema: non si puo' gestire con un elemento dello schema, verrebbe gestito da trigger
Si tratta di scegliere se far si che la base di dati mostri la situazione attuale, o tenga uno storico di come si e' evoluta la situazione nel tempo.
TODO vedi l'esempio dagli appunti del prof su come cambia lo schema ER
Vincoli che riguardano delle specifiche entità.
"Negli ospedali lavorano diverse figure professionali, tra le quali distinguiamo: medici, infermieri, dirigenti, personale amministrativo"
Obiettivo e' avere una entità che generalizzi le diverse figure.
La gerarchia ha due caratteristiche, che vanno sempre specificate (una per coppia):
Le sottoclassi possono non avere identificatore, nel caso questo sia presente nella superclasse.
Quando ci sono attributi che sono in comune tra le relazioni e' bene chiedersi se si e' in presenza di una possibile gerarchia di generalizzazione.
"Si sappia che un Dipendente può essere Medico, Dirigente, Infermiere, Amministrativo e Custode"
TODO aggiungi questo esempio
TODO prendi ER dagli appunti del prof
TODO prova a fare lo schema sui dati immobiliari proposto dal prof e dopo guarda la soluzione
La base di dati dovrà memorizzare:
TODO rifallo da solo e controlla sia rispetto alla versione dell'anno scorso che rispetto a quella di questo anno
Come abbiamo proceduto a lezione:
Da modello concettuale a modello logico.
TODO integra con il diagramma che avevi fatto e mettilo qua
TODO integra con gli esempi nel materiale del prof su Ariel
Partiamo da questo
Chiavi primarie sono sottolineate con riga continua. TODO metterlo in latex
Attributi opzionali sono denotati da un
Parto da una entità -
Scrivo i suoi attributi -
Prossima entità -
Prossima entità (focus solo sugli attributi interessanti) -
Scegliendo
Gestione attributi composti.
Per un attributo multi-valore come
Quindi ottengo una nuova relazione -
1 a N: metto l'identificatore nel lato 1 che ho nel lato N
Con la seguente notazione specifico a quale chiave fa riferimento la chiave esterna:
vedo se ci sono associazioni rimanenti
parto da
Prossima associazione
Abbiamo sbagliato il modello concettuale, manca
LEFT JOIN che vado a pescare questi record quando mi serve.
In realtà sono sbagliate le (1,N) perché vorrebbe dire che ogni volta che aggiungo un
Nel caso di entità deboli faccio prima l'entità a cui sono associate.
Per convenienza e' possibile mettere un JOIN non sono un delirio di ON a causa dei molteplici attributi coinvolti nella chiave.
E' una scelta fatta in fase di ristrutturazione.
Per una associazione con N entità devo prendere tutte le chiavi di ogni entità partecipante, per poi metterla nella nuova tabella.
TODO mettere immagini da appunti del prof
Si può sempre fare, con ogni tipo di gerarchia.
La gerarchia collassa in un'unica entità, che e' quella padre e:
Non e' praticabile con una gerarchia parziale.
La gerarchia viene spostata verso il basso, tutti gli attributi del padre vengono dati ai figli.
Se non sappiamo ad esempio se una persona e' fisica o giuridica dobbiamo fare una UNION per cercarla dappertutto.
La gerarchia viene scomposta in associazioni binarie (
I figli sono sempre entità deboli rispetto al padre.
cssclasses:
- cornell-note
- flex
tags:
- relational-algebra
- sqlLinguaggio di interrogazione procedurale che definisce le operazioni necessarie per estrarre dati da una o più relazioni di un database.
Qualunque operazione produce una relazione.
DQL, quindi non si modificano i dati, si interrogano solamente.
Una tupla alla volta: non abbiamo possibilità di vedere tutti i dati assieme, ma solo una tupla alla volta, quindi le dobbiamo mettere a fianco una all'altra.
Operatori unari:
Operatori binari insiemistici:
Operatori binari di join:
Ricorda che le relazioni in un database sono insiemi, vedi Modello relazionale.
Precondizioni
Per poter fare unione, intersezione, differenza le relazioni devono avere pari grado, e attributo per attributo sono compatibili (le relazioni che partecipano non possono avere vincoli di dominio diversi).
Date
Produce il medesimo grado della relazione di partenza e cardinalità pari a cardinalità di
Il nome della colonna e' quello della relazione di sinistra.
Se avessi
E ne facessi l'unione otterrei entrambi i record perché sono due individui diversi.
Date
Produce il medesimo grado della relazione di partenza e cardinalità pari agli elementi in comune.
Date
La differenza non e' una operazione simmetrica.
Risponde alla domanda "Quali record della relazione voglio considerare?"
Accetta un predicato, quindi posso usare AND, OR, NOT; conserva il grado e la cardinalità e' minore o uguale a quella di partenza.
Esempio: voglio trovare le pellicole del 2010
Scorre i record uno a uno, quindi e' una valutazione locale, non globale, quindi non può essere usata per quei casi dove si vogliono ottenere valori relativi al globale.
Seleziona solo l'insieme di attributi che voglio mostrare nel risultato. Riduce il grado e la cardinalità e' uguale a quella di partenza.
E' possibile che applicando una proiezione si possano ottenere righe uguali, in questo caso record uguali collassano in uno solo.
Chiedersi sempre se e' il caso di ridurre gli attributi applicando una proiezione.
| A | B |
|---|---|
| a1 | b1 |
| a2 | b3 |
| C | D |
|---|---|
| c1 | d1 |
| c2 | d2 |
| c3 | d3 |
| A | B | C | D | |
|---|---|---|---|---|
| a1 | b1 | c1 | d1 | |
| a1 | b1 | c2 | d2 | |
| a1 | b1 | c3 | d3 | |
| a2 | b2 | c1 | d1 | |
| a2 | b2 | c2 | d2 | |
| a2 | b2 | c3 | d3 |
Data
Il prodotto cartesiano ritorna tutte le combinazioni, ma non tutte le righe concorrono al risultato di questo join, solo quelle dove la chiave primaria della relazione di sinistra e la chiave esterna della relazione di destra sono le stesse.
Quindi e' una selezione sul risultato del prodotto cartesiano.
Join che hanno una uguaglianza come predicato.
Equi join che considera l'eguaglianza degli attributi con il medesimo nome nelle due relazioni. Eventuali attributi con lo stesso nome collassano in uno solo.
Come si vede nell'ultima riga
Vediamo questo operatore con un esempio
Permettere di evidenziare una interazione tra tuple di due tabelle: "vuole restituire quei record della tabella
Viene definita come l'opposto al prodotto cartesiano.
Numeratore e denominatore
Al denominatore metto elementi che vogliamo siano in relazione con il soggetto di "tutti".
Al numeratore metto una relazione i cui attributi contengono gli attributi al denominatore, e che voglio portare nel risultato .
Dal punto di vista formale, date due relazioni
Nell'esempio vogliamo trovare un record che ha
Altro esempio
| a | b |
| --- | --- |
| a1 | b1 |
| a2 | b1 |
| a1 | b2 |
| b |
| --- |
| b1 |
| b2 |
Dobbiamo trovare un valore di
| a |
| --- |
| a1 |
La divisione e' utile quando non so a priori cosa ho a denominatore.
Date due relazioni
If
Per "trovare tutti gli X che Y", posso:
Per "trovare gli X che sono Y in tutti gli Z", posso:
Cercare di limitare quanto più possibile il grado di una relazione, in modo da fare che le relazioni si possano combinare più facilmente.
Procedura:
Sto cercando l'assenza di qualcosa, quindi devo usare la sottrazione o un join esterno.
Usare la proiezione per ottenere relazioni confrontabili in termini di sottrazione
Siccome
Procedura:
trovo le pellicole di genere "thriller"
prendo l'id dalla relazione precedente
trovo le pellicole di genere "crime"
prendo l'id dalla relazione precedente
unisco le due relazioni ottenute
Trovare le pellicole che sono di genere "comedy" e "romantic"
Appartenere ad entrambi i generi vuol dire avere due record in cui c'e' il codice della pellicola e ognuno dei due "genre" voluti
Procedura:
E' sbagliato perché il risultato e' simile a
movie_comedy
| id | genre |
|---|---|
| 1 | comedy |
| 2 | comedy |
movie_romance
| id | genre |
|---|---|
| 3 | romance |
| 1 | romance |
Che non produce il risultato sperato perché non ci sono record uguali su cui fare intersezione, quindi devo usare la proiezione:
Soluzioni alternative? No!
In questo caso un record ha entrambi gli stessi attributi, quindi posso usare
Posso usare l'atomizzazione delle selezioni per ottimizzare la query (l'operazione più selettiva va fatta per prima per poter dare alla seconda un set più piccolo possibile su cui lavorare)
Procediamo per step
Guardando lo schema IMDB dato a lezione
Trovare il titolo delle pellicole con valutazione (rating) maggiore di 8
Trovare le pellicole thriller con valutazione sopra 8
Trovare il nome dei registi di film thriller
Trovare i film le cui recensioni sono sempre superiori a 8 (non immediato!)
Trovare le pellicole distribuite (released) sia in USA sia in FRA
Questa e' sbagliata, perché potrei avere una situazione di questo tipo
| movie | country |
|---|---|
| m1 | FRA |
| m1 | GBR |
| m2 | FRA |
Non ci si può accontentare della selezione per via della presenza di
Il risultato e' definito sulle colonne
Funzionerebbe anche con l'intersezione in questo caso, ma il vantaggio della divisione e' che non e' necessario specificare a priori come e' composto il divisore.
A: seleziono città con più di 500k abitanti
B: seleziono da govern i politici che hanno governato le città in A
tolgo da politician i politici in B
Ha senso prendere da politician piuttosto che da govern come sottrazione di partenza, perché voglio tenere anche quelli che non hanno mai governato.
Trovare il nome delle città in cui non e' utilizzato il dollaro come moneta
Trovare i paesi che non confinano con l'italia
A: trovare i paesi che confinano con ITA
B: sottrarre A dall'elenco di tutti i country
Devo ottenere
Non c'e' bisogno di ridenominare l'id in
Se non togliamo
Trovare le citta' governate da piu' di un politico dopo il 2020
Essendo che non siamo in grado di contare possiamo mettere in join
Trovare città che sono state governate da politici sia di destra sia di sinistra
Soluzione alternativa con join
Trovare le pellicole prodotte in due paesi diversi (e' un self join, rivedi come si fa a chiamare in modi diversi la stessa tabella)
cssclasses:
tags:
- italian
- testingDebuggare o capire codice e' 3 volte più difficile che scrivere codice, quindi se qualcuno scrive codice al massimo della sua capacita' e' per definizione incapace di debuggarlo o capirlo.
Quindi scrivi codice semplice.
Convalida: confronto del software con i requisiti informali posti dal committente.
Test di accettazione.
Verifica: confronto del software con le specifiche formali prodotte dall'analista.
Test di unita'.
Funzionamento non corretto del programma, non del suo codice.
Esterno al sistema.
Quindi dire "c'e' un malfunzionamento alla riga 42" e' un uso improprio del termine.
Dire "invocando somma con 1 e 2 produce un malfunzionamento perche' si ottiene 2"
Non e' il comportamento previsto. Ad esempio l'Ariane 5 che esplode e' chiaro sia un malfunzionamento.
Legato al codice, e' condizione necessaria (ma non sufficiente) per il verificarsi di un malfunzionamento.
Ad esempio se ho una funzione raddoppia che fa
int raddoppia(int n) {
return n * n;
}
non sempre ritorna un risultato sbagliato, per 2 e' corretta, per 3 no.
Pericolose perche' sembra stiano funzionando.
Sempre in Ariane 5 anomalia e' stata la conversione di un 64bit a 16bit del valore della velocita' orizzontale.
Causa di un difetto. In genere si tratta di un errore umano (concettuale, battitura, scarsa conoscenza del linguaggio).
Il riutilizzo della parte incriminata dall'Ariane 4 per l'Ariane 5, perche' Ariane 4 raggiungeva velocita' orizzontali non rappresentabili con 16bit.
E' possibile evitare che si ripeta, con dei processi.
Tecniche statiche: basate sull'analisi del codice:
Tecniche dinamiche: basate sull'esecuzione del programma eseguibile
Classificazione delle tecniche:
Metodi formali: tecniche che si prefiggono di provare l'assenza di anomalie nel prodotto finale
Testing: tecniche che si prefiggono di rilevare malfunzionamenti, o fornire fiducia nel prodotto
Debugging: tecniche che si prefiggono di localizzare le anomalie che causano malfunzionamenti rilevati in precedenza
Testing e' verifica di correttezza o validazione di affidabilita'.
Un programma si puo' dire corretto se aderisce alle specifiche per ogni dato appartenente al dominio di ingresso.
Un test ha successo quando riesce a rilevare uno o piu' malfunzionamenti presenti nel programma che non fossero gia' noti.
Test ideale: se il superamento del test (quindi il suo non successo) implica la correttezza del programma
Presi due test un criterio si dice affidabile se entrambi i test hanno successo o entrambi falliscono.
Presi due test un criterio si dice valido se, qualora
Dato
int raddoppia(int n) {
return n * n;
}
Un criterio che seleziona sottoinsiemi di
Un criterio che seleziona sottoinsiemi di
Un criterio che seleziona sottoinsiemi finiti di
Cosa succede se ho un test affidabile e valido? (
Quindi:
Allora implica che il programma e' corretto, ma questa e' la definizione di test ideale, che abbiamo detto che non esiste.
Quindi non può esistere un criterio che sia contemporaneamente affidabile e valido!
Quindi l'unico caso in cui ho
Quindi l'unico caso in cui ho
Vogliamo quindi arrivare ad avvicinarci.
Dobbiamo trovare una metrica che misuri la copertura di un criterio e ci permetta di
Un caso di test per evidenziare un malfunzionamento causato da una anomalia deve:
Deriva dal punto 1. che voglio assicurarmi di stimolare tutti i comandi.
Voglio che i miei test siano limitati in merito al cammino che coprono, in modo tale che mi sia più agevole utilizzarli come punto di partenza per il debugging.
Un test
Soddisfare questo criterio non garantisce la correttezza del programma, esegue semplicemente tutte le righe di codice raggiungibili.
if, while, ...)Un test
Implica il criterio di copertura dei comandi.
if)Un test
Si differenzia dal precedente Criterio di copertura delle decisioni perché si riferisce alle singole condizioni, non a tutta l'espressione oggetto di valutazione della condizione.
Non implica il criterio di copertura dei comandi.
Un test
E' l'intersezione di Criterio di copertura delle decisioni (e quindi del Criterio di copertura dei comandi) e Criterio di copertura delle condizioni.
Un test
Per esempio per la condizione x != 0 && y < 3, vengono testati separatamente i casi
Data la natura combinatoria del criterio e' considerato non applicabile in pratica, oltre che alcune combinazioni potrebbero non avvenire, e quindi non avrebbe alcun senso testarle.
Conto solo le combinazioni piu' rilevanti, cioe' quelle per cui la modifica di una condizione base porti a modificare l'esito della decisione.
Permette quindi di testare solamente per quei valori significativi che vanno a far cambiare la decisione.
Un test
Non applicabile in pratica.
Un test
Il criterio viene spesso applicato nella forma
E' analisi statica. Gli elementi da analizzare non sono infiniti come invece possono esserlo gli elementi nell'analisi dinamica.
Serve a capire come ottimizzare il codice, e per identificare potenziali errori.
Ci sono tre operazioni:
Analizziamo le operazioni su x, x1, x2
void swap(int &x1, int &x2) {
int x1; // (a) - shadowing di x1 parametro
x3 = x1; // (u)
x3 = x2;
x2 = x1; // (u)
} // (a) - scope di x1 terminato
| variabile | DF |
|---|---|
| x | auua |
| x1 | ...dud... |
| x2 | ...ddd... |
x viene usata 2 volte senza essere prima stata definita. x2 viene definita più volte senza essere usata nel frattempo.
Lista di stati non corretti:
Un test
Ovvero, in termini umani: esiste almeno un uso di quella definizione.
Esempio
void main() {
float a, b, x, y;
read(x);
read(y);
a = x;
b = y;
while (a != b)
if (a > b)
a = a - b;
else
b = b - a;
write(a);
}
Considerando la variabile aTODO AGGIUNGERE LINE NUMBERS
CHIEDERE A NOTEBOOK ULTERIORI ESEMPI E DOMANE SU QUESTA PARTE
Un test
Non copre istruzioni che non sono usi.
CHIEDERE A NOTEBOOK ULTERIORI ESEMPI E DOMANE SU QUESTA PARTE
Esempio
void main() {
float a, b, x, y;
read(x);
read(y);
a = x;
b = y;
while (a != b)
if (a > b)
a = a - b;
else
b = b - a;
write(a);
}
Considerando ancora la variabile
CHIEDERE A NOTEBOOK DI FARE UN ESEMPIO Di
Inseriamo
Quindi nel mentre cerca questi errori ci sono ottime probabilità che ne trovi altri.
Tipicamente in questo scenario c'e' un team di testing separato dal team di sviluppo.
Problemi
Viene generato un insieme di programmi
Su di essi viene eseguito lo stesso test
Un test
La metrica e' la frazione di mutanti riconosciuta come diversa da
Voglio un mutante per ogni possibile difetto, virtualmente infiniti. I più semplici effettuano modifiche sintattiche che comportino modifiche semantiche.
L'onere di esecuzione e' molto forte.
Chiedere a NOTEBOOK di espandere questa parte
Isolare la classe: costruiamo stub per renderla eseguibile indipendentemente dal contesto
I mock consentono di parallelizzare lo sviluppo di componenti che dipendono l'una dall'altra, a patto di avere dei contratti, espressi con le interfacce.
Chiedere a NOTEBOOK
Sinonimo di blackbox.
Punto di partenza con cui effettuare ragionamenti per la copertura. Parto dai requisiti.
tags:
- software-engineering
- italianDomande
TDD is an awareness of the gap between decision and feedback during programming, and techniques to control that gaps
-- Kent Beck
The gap is the time between when a decision is made and when you get if it was correct. In traditional development this gap could be huge, which makes debugging difficult and costly.
Techniques:
So TDD could be thought as a feedback mechanism.
Shrinking the gap allows to reduce decision fatigue and technical debt, as the latter could be expressed as decisions made without proper feedback.
TDD e' una metodologia di sviluppo software, non di testing
Testing shows the presence, not the absence of bugs.
-- Dijkstra
This quote comes from here
Write tests until fear is transformed into boredom
TDD = test first + baby steps
Vedere Verifica e convalida per
SUT is the subject under test, there can be only one SUT for a single test and it is never mocked.
DOC is the dependent on component, there can be many and they have to be mocked.
Si possono evitare di mockare:
Failure is progress
Ask myself: "What set of tests, when passed, will demonstrate the presence of code we are confident will compute as expected?"
The rhythm of TDD (Red Green Refactor Red Green ...):
If in need to have a test go red use the triangulation technique, for example suppose the first expectation was the only one then we add the second:
public void testEquality() {
assertTrue(new Dollar(5).equals(new Dollar(5)));
assertTrue(new Dollar(5).equals(new Dollar(6))); // triangulation
}
Per ogni test ci deve essere una sola esecuzione del metodo che sto testando, cosi che quando qualcosa fallisce so esattamente dove andare a guardare, e non ci sono test che "falliscono a meta'".
Modifiche del codice senza cambiare funzionalita', per modificare qualche qualita' interna, avviene dopo il Green perche' se ottengo un Red dopo un Refactoring sono nell'incertezza.
Continuare a fare refactoring senza pieta'. Perche' per farlo ci vuole coraggio: "Scrivo il test e tra 5 10 minuti il test passa"
Non ci può essere un refactoring se non si parte da Green.
Nei refactoring non si possono modificare funzionalità.
Un rosso deve essere ragionevolmente breve, indicativamente minore di 10' - 15'.
Trovarsi bloccati in questa fase puo' voler dire che si e' approcciato un problema troppo difficile, trovare una via piu' semplice.
The notion comes from "stunt double" used in movies.
Test double - controfigura per il testing: si mette al posto di DOC per testare in isolamento il SUT; utile quando DOC:
dummy: oggetto passato ma su cui non si fa alcuna asserzione
fake: possiede una implementazione funzionante, ma vive solo nel mondo dei test, ad esempio un database in memory per velocizzare i test
assenza di mock: si sta facendo verificazione dello stato
mock: verifica del comportamento ("questo metodo e' stato chiamato cosi?")
stub: forniscono risposte preconfezionate, e rispondono solo a quelle (parto dal vuoto)
spy: sono proxy di oggetti reali che consentono di loggarne le interazioni
Crea un oggetto. Dichiaro cosa voglio che questo oggetto sappia fare.
Instrumentano il DOC, instrumentati per essere interrogabili in merito a cosa gli e' successo: "chi ti ha chiamato?", "in che ordine?", "quante volte?", ...
when(mockedObj.methodName(args)).thenXXX(values);
Per verificare quante volte un metodo viene chiamato
verify(mockedClass, times(1)).methodName(args)
// oppure
ArgumentCaptor<Person> arg = ArgumentCaptor
.forClass(Person.class);
verify(mock).doSomething(arg.capture());
assertEquals("John", arg.getValue().getName());
Per evitare di "consumare l'iteratore" si puo' utilizzare questo metodo di utilita':
public static <T> void whenIter(Iterable<T> p, T... d) {
when(p.iterator())
.thenAnswer((Answer<Iterator<T>>) _ ->
List.of(d).iterator());
}
Per mockare un costruttore
// si puo' passare a mockConstruction una serie di
// parametri per mockare ad esempio eventuali altri
// metodi che vengono usati nel costruttore di Tavolo
try (var mocked = Mockito.mockConstruction(Tavolo.class)) {
Partita p = new Partita();
Card c = mock();
Tavolo t = mocked.constructed().getFirst();
// ...
}
Wrappa un oggetto reale. Dichiaro cosa voglio che non sappia fare (svuoto).
Se il soggetto e' l'oggetto under test allora
stub : spy = riempire : svuotare
Meglio partire dal vuoto, cioe' stub.
Sono testati indirettamente, a fronte di chiamate dall'esterno.
Voglio poter cambiare i metodi privati senza troppo sforzo, in modo da fare refactor senza incorrere in grossi attriti.
Usare extracting quando si vuole testare una proprieta' privata, e
AssertionsForClassTypes.assertThat(player)
.extracting("personalDeck", as(LIST))
.containsExactly(Card.of("1B"), Card.of("2B"));
quando si sta testando una classe che implementa interfacce, perche' altrimenti l'assertThat "solito" non riesce a estrarre la proprieta'.
var m = Game
.class
.getDeclaredMethod("distributeInitialCards");
m.setAccessible(true);
m.invoke(game);
Da farsi solo in test, perche' altrimenti a causa della reflection puo' rompere tutte le astrazioni.
Il codice duplicato e' anche dentro i test, non solo nel sorgente!
Good tests are written in isolation, so if one fails the rest will continue as if nothing happened: the idea is not to pollute a global state from which tests take their fixtures.
One broken test
Two broken tests
If tests are written in isolation then the order in which they're run does not matter.
Ariadne's thread: Offload your brain list into a written one, on paper, jot down critical issues, pain points, etc... ; you don't want to put the list into tests right away, always follow the "climber rule": out of four among feet and hands always have at least three attached to the wall.
Write the assert first and then work your way upwards through the test.
This approach allows to concentrate on the goal and force the preconditions to come out almost on their own.
Make use of data to tell a clearer story, make your intentions evident.
For example split whole numbers in elementary operations to make the reader aware of "where did that 372.68 come from?".
Sempre bene usare test di integrazione per asserire riguardo la correttezza del programma.
A volte puo' succedere che eseguendo un nuovo test (scommentato ad esempio come vediamo a laboratorio), questo passi senza aver seguito il red green refactor red etc.Red green green ... wat?
Vuol dire che magari la mia soluzione probabilmente ha implementato piu' del necessario.
Quindi vuol dire che non ho scritto la soluzione piu' semplice per far passare il test.
I want to make a learning exercise with you: I need to learn TDD for a university course, I want you to act like you're not into it and will try to push back, while I will try to convince you. Ready?